// ---------------------------------------------------------------------------------------------------------------------
// BRANCH TARGET_PREDICTOR.h
// ---------------------------------------------------------------------------------------------------------------------
// Project:	Alpha Branch Predictor
// Authors:	Jabeer Ahmed, 
// 			Omar Mohsin, 
// 			Fadi Butti
// Course:	ECE - 586 (Winter 2013)
// Prof:	Mark Faust
// Portland State University
// ---------------------------------------------------------------------------------------------------------------------

#ifndef __BRANCH_TARGET_PREDICTOR_H__
#define __BRANCH_TARGET_PREDICTOR_H__

#include <vector>
#include <math.h>

typedef unsigned int uint;

class branch_target_entry{
	uint target_ins_addr;		// The target instruction address
	uint tag;					// Tag bits
	uint shift_tag_bits;		// num tag bits
	bool valid;					// 1 bit for valid/invalid
	uint size;
	
public:	
	
	branch_target_entry(int tagsize){
		shift_tag_bits = 32-tagsize;	// Number of shifts needed to get tag from PC
		tag = 0;						// Set tag to 0
		valid = 0;						// valid to 0
		target_ins_addr = 0;			// 
		size = tagsize + 32 + 1;		// size of entry = tagsize + 32 bits target + 1 bit valid
	}
 	
	uint get_target_address(unsigned int pc){
		if (valid && (tag == (pc >> shift_tag_bits)))
			return target_ins_addr;
		else	
			return 0x0;
	}
	
	uint set_target_address(uint target_addr){
		tag = (target_addr >> shift_tag_bits);
		target_ins_addr = target_addr;
		valid = true;
		return target_ins_addr;
	}
};

struct branch_target_predictor {

	vector <branch_target_entry> table;	// Branch target prediction table;
	uint size;							// Size of the table
	uint num_tag_bits;					// Size of tag
    uint num_indx_bits;
    
	branch_target_predictor(int length)
	{
		size = length;
		num_indx_bits = log(size)/log(2);
        num_tag_bits = 32 - num_indx_bits;
		for (int i=0; i<size; i++)
			table.push_back(branch_target_entry(num_tag_bits));		
	}
	uint indexFor(uint pc)
	{
		return (pc>>2) & (size-1);
	}
	
	uint get_prediction(uint pc)
	{
		uint indx = indexFor(pc);
		uint addr = table[indx].get_target_address(pc);
		return addr;
	}
	
	void set_prediction(uint pc)
	{
		uint indx = indexFor(pc);
		table[indx].set_target_address(pc);
	}
};

#endif 	// __BRANCH_TARGET_PREDICTOR_H__